#include "stdafx.h"
#include "CircBuf.h"

#define MIN(a, b)	(a) < (b) ? (a) : (b)

CCircularBuf::CCircularBuf(DWORD dwSize, BOOL bOverwrite)
{
    m_pchBuf = (PUCHAR)malloc(dwSize);
    m_dwSize = dwSize;
    m_dwReadPointer = m_dwWritePointer = 0;
    m_bOverflow = FALSE;
    m_bOverWrite = bOverwrite;
    m_bEmpty = TRUE;
    init_mutex(&m_CS);
}

CCircularBuf::~CCircularBuf()
{
    if (m_pchBuf != NULL)
    {
        free(m_pchBuf);
    }

    delete_mutex(&m_CS);
}

void CCircularBuf::Reset(void)
{
    lock_mutex(&m_CS);
    m_dwReadPointer = m_dwWritePointer = 0;
    m_bOverflow = FALSE;
    m_bEmpty = TRUE;
    unlock_mutex(&m_CS);
}

BOOL CCircularBuf::IsEmpty(void)
{
    BOOL bRet;
    lock_mutex(&m_CS);
    bRet = m_bEmpty;
    unlock_mutex(&m_CS);
    return bRet;
}

BOOL CCircularBuf::IsOverflow(void)
{
    BOOL bRet;
    lock_mutex(&m_CS);
    bRet = m_bOverflow;
    unlock_mutex(&m_CS);
    return bRet;
}

void CCircularBuf::ClearOverflow(void)
{
    lock_mutex(&m_CS);
    m_bOverflow = FALSE;
    unlock_mutex(&m_CS);
}

DWORD CCircularBuf::APKGetFreeSpace(void)
{
    DWORD dwFree;

    if (m_bEmpty)
    {
        dwFree = m_dwSize;
    }
    else if (m_dwReadPointer < m_dwWritePointer)
    {
        dwFree = m_dwSize - m_dwWritePointer + m_dwReadPointer;
    }
    else
    {
        dwFree = m_dwReadPointer - m_dwWritePointer;
    }

    return dwFree;
}

DWORD CCircularBuf::GetDataSize(void)
{
	return m_dwSize-APKGetFreeSpace();
}

BOOL CCircularBuf::IsFull(void)
{
    BOOL bRet;
    lock_mutex(&m_CS);
    bRet = (APKGetFreeSpace() == 0);
    unlock_mutex(&m_CS);
    return bRet;
}

DWORD CCircularBuf::Read(PUCHAR pBuf, DWORD dwBufLength)
{
    DWORD dwTop, dwRead;

    if (m_bEmpty)
    {
        return 0;
    }

    // read length can't be more than the buffer size
    dwRead = MIN(dwBufLength, m_dwSize);

    lock_mutex(&m_CS);

    dwRead = MIN(dwRead, m_dwSize - APKGetFreeSpace());

    //RETAILMSG (1, (TEXT("CCircularBuf::Read|before readpos = %d writepos = %d.\n"), m_dwReadPointer, m_dwWritePointer));
    //RETAILMSG (1, (TEXT("CCircularBuf::Read|before dwRead = %d empty = %d.\n"), dwRead, m_bEmpty));
    dwTop = MIN(dwRead, (m_dwSize - m_dwReadPointer));

    memcpy(pBuf, (m_pchBuf + m_dwReadPointer), dwTop);

    if (dwRead > dwTop)
    {
        memcpy((pBuf + dwTop), m_pchBuf, (dwRead - dwTop));
    }

    // update read pointer
    m_dwReadPointer += dwRead;

    if (m_dwReadPointer >= m_dwSize)
    {
        m_dwReadPointer -= m_dwSize;
    }

    if (m_dwReadPointer == m_dwWritePointer)
    {
        m_bEmpty = TRUE;
    }

    unlock_mutex(&m_CS);

    //RETAILMSG (1, (TEXT("CCircularBuf::Read|after readpos = %d writepos = %d.\n"), m_dwReadPointer, m_dwWritePointer));
    //RETAILMSG (1, (TEXT("CCircularBuf::Read|after dwRead = %d empty = %d.\n"), dwRead, m_bEmpty));
    //RETAILMSG (1, (TEXT("CCircularBuf::Read|after <%hs>\n"), pBuf));

    return dwRead;
}

DWORD CCircularBuf::Write(PUCHAR pBuf, DWORD dwBufLength)
{
    DWORD dwWrite, dwTop, dwFree, bOverflow;

    bOverflow = FALSE;

    if (dwBufLength == 0)
    {
        return 0;
    }

    // if the write data is more than the buffer size
    // we only write the the latest data into the buffer
    // and discard useless data from the head of input buffer
    if (dwBufLength > m_dwSize)
    {
        pBuf += (dwBufLength - m_dwSize);
        dwWrite = m_dwSize;
    }
    else
    {
        dwWrite = dwBufLength;
    }

    lock_mutex(&m_CS);

    dwFree = APKGetFreeSpace();

    if ((dwWrite > dwFree) && (m_bOverWrite))
    {
        bOverflow = m_bOverflow = TRUE;
        //RETAILMSG (1, (TEXT("overflow dwFree = %d.\n"), dwFree));
    }
    else if ((dwWrite > dwFree) && (!m_bOverWrite))
    {
        pBuf += (dwWrite - dwFree);
        dwWrite = dwFree;
    }

    //RETAILMSG (1, (TEXT("CCircularBuf::Write|before readpos = %d writepos = %d.\n"), m_dwReadPointer, m_dwWritePointer));
    //RETAILMSG (1, (TEXT("CCircularBuf::Write|before dwWrite = %d empty = %d.\n"), dwWrite, m_bEmpty));
    dwTop = MIN(dwWrite, (m_dwSize - m_dwWritePointer));

    memcpy((m_pchBuf + m_dwWritePointer), pBuf, dwTop);

    if (dwWrite > dwTop)
    {
        memcpy(m_pchBuf, pBuf + dwTop, (dwWrite - dwTop));
    }

    // update write pointer
    m_dwWritePointer += dwWrite;

    if (m_dwWritePointer >= m_dwSize)
    {
        m_dwWritePointer -= m_dwSize;
    }

    m_bEmpty = FALSE;

    if (bOverflow)
    {
        m_dwReadPointer = m_dwWritePointer;
    }

    unlock_mutex(&m_CS);

    //RETAILMSG (1, (TEXT("CCircularBuf::Write|after readpos = %d writepos = %d.\n"), m_dwReadPointer, m_dwWritePointer));
    //RETAILMSG (1, (TEXT("CCircularBuf::Write|after dwWrite = %d empty = %d.\n"), dwWrite, m_bEmpty));


    return dwWrite;
}

